#include <tcPhysics/tc_physics.h>
#include <Box2D/Box2D.h>
#include <tcDatabase/tc_chessboard.h>
#include <tcSound/tc_sound.h>

#pragma warning(disable: 4305)
#pragma warning(disable: 4800)

#define PTM_RATIO 32.0f

static b2World* g_pWorld = NULL;
static b2Body* g_bodyList[TC_CHESSMEN_MAX_NUMBER];
static b2Fixture* g_hingeFixtureList[2];

static const float RADIUS = 26.0f;

static const float SCALE_LARGE = 0.67f;
static const float SCALE_MIDDLE = 0.47f;
static const float SCALE_SMALL = 0.35f;

class MyContactListener : public b2ContactListener 
{
public:
	virtual void BeginContact(b2Contact* contact)
	{
		if( contact->GetFixtureA() != g_hingeFixtureList[0] && contact->GetFixtureA() != g_hingeFixtureList[1] )
		{
			tc_sound_play(TC_SND_HIT_STONE);
			b2Body *body = contact->GetFixtureA()->GetBody();
			b2Vec2 impulse_vec = body->GetLinearVelocity();
			body->SetLinearVelocity(0.85f * impulse_vec);
			body = contact->GetFixtureB()->GetBody();
			impulse_vec = body->GetLinearVelocity();
			body->SetLinearVelocity(0.85f * impulse_vec);
		}
		else
		{
			tc_sound_play(TC_SND_HIT_HINGE);
		}
	}
};


static b2Body* get_chessman_body(int id)
{
	return g_bodyList[id];
}

void tc_physics_init()
{
	memset(g_bodyList, 0, sizeof(g_bodyList));
	memset(g_hingeFixtureList, 0, sizeof(g_hingeFixtureList));

	b2Vec2 gravity = b2Vec2(0.0f, 0.0f);
	bool doSleep = true;
	g_pWorld = new b2World(gravity);
	MyContactListener* listener = new MyContactListener;
	g_pWorld->SetContactListener(listener);
	g_pWorld->SetAllowSleeping(doSleep);
	g_pWorld->SetContinuousPhysics(true);
}

void tc_physics_exit()
{
	
}


void tc_physics_update( float dt )
{
	g_pWorld->Step(dt, 10, 10);
}


void tc_physics_create_hinge( tc_vector2 position, float width, float height )
{
	static int index = 0;
	if (index > 1)
	{
		return;
	}

	b2BodyDef spriteBodyDef;
	spriteBodyDef.type = b2_staticBody;
	spriteBodyDef.position.Set(position.x/PTM_RATIO, position.y/PTM_RATIO);
	b2Body *spriteBody = g_pWorld->CreateBody(&spriteBodyDef);

	b2PolygonShape spriteShape;
	spriteShape.SetAsBox(width/PTM_RATIO/2, height/PTM_RATIO/2);
	b2FixtureDef spriteShapeDef;
	spriteShapeDef.shape = &spriteShape;
	b2Fixture *fixture = spriteBody->CreateFixture(&spriteShapeDef);

	g_hingeFixtureList[index++] = fixture;
}



static float get_radius_from_size(tcType size)
{
	float scale = 1.0;
	switch (size) {
	case TC_CHESSMAN_SIZE_SMALL:
		scale = SCALE_SMALL;
		break;
	case TC_CHESSMAN_SIZE_MIDDLE:
		scale = SCALE_MIDDLE;
		break;
	case TC_CHESSMAN_SIZE_LARGE:
		scale = SCALE_LARGE;
		break;
	}
	return scale * RADIUS;
}

void tc_physics_adjust_body_size(int id)
{
	tcType size = tc_chessman_get_size(id);
	float radius = get_radius_from_size(size);
	b2Body *body = get_chessman_body(id);
	if (body->GetFixtureList())
	{
		body->DestroyFixture(body->GetFixtureList());
	}

	b2CircleShape circle;
	circle.m_radius = radius/PTM_RATIO;

	b2FixtureDef spriteShapeDef;
	spriteShapeDef.shape = &circle;
	spriteShapeDef.density = 10.0f;
	spriteShapeDef.friction = 20.0f;
	spriteShapeDef.restitution = 1.0f;
	body->CreateFixture(&spriteShapeDef);

	b2MassData massData;
	body->GetMassData(&massData);
	massData.mass = 3.0f;
	body->SetMassData(&massData);

	float linearDampings[] = {4.0f, 2.5f, 1.0f};
	body->SetLinearDamping(linearDampings[size]);
}

void tc_chessman_create_body(int id)
{	
	b2BodyDef bodyDef;
	bodyDef.bullet = true;
	bodyDef.type = b2_dynamicBody;
	bodyDef.position.Set(0, 0);
	bodyDef.angularDamping = 2.0f;
	bodyDef.allowSleep = true;
	b2Body *body = g_pWorld->CreateBody(&bodyDef);
	g_bodyList[id] = body;
	tc_physics_adjust_body_size(id);
}

void tc_chessman_destroy_body( int id )
{
	b2Body *body = get_chessman_body(id);
	if (body)
	{
		g_pWorld->DestroyBody(body);
		g_bodyList[id] = NULL;
	}
}


void tc_chessman_set_body_active( int id, BOOL active )
{
	b2Body *body = get_chessman_body(id);
	body->SetActive(active);
}


void tc_chessman_set_transform(int id, tc_vector2 position, float rotation )
{
	b2Vec2 pos;
	pos.x = position.x / PTM_RATIO;
	pos.y = position.y / PTM_RATIO;
	get_chessman_body(id)->SetTransform(pos, rotation);
	b2Vec2 speed;
	speed.SetZero();
	get_chessman_body(id)->SetLinearVelocity(speed);
	get_chessman_body(id)->SetAngularVelocity(0);
}

void tc_chessman_get_position( int id, tc_vector2 *position)
{
	b2Vec2 pos = get_chessman_body(id)->GetPosition();
	position->x = pos.x * PTM_RATIO;
	position->y = pos.y * PTM_RATIO;
}

float tc_chessman_get_rotation( int id )
{
	b2Body *body = get_chessman_body(id);
	float rotation = body->GetAngle();
	rotation *= 180 / M_PI;
	rotation *= -1;
	return rotation;
}

float tc_chessman_get_radius(int id)
{
	tcType size = tc_chessman_get_size(id);
	return get_radius_from_size(size);
}

void tc_chessman_apply_impulse( int id, tc_vector2 impulse )
{
	b2Body *body = get_chessman_body(id);
	b2Vec2 imps_vec(impulse.x, impulse.y);
	body->ApplyLinearImpulse(imps_vec, body->GetPosition());
}

void tc_chessman_get_velocity( int id, tc_vector2 *velocity )
{
	b2Vec2 vec;
	b2Body *body = get_chessman_body(id);
	vec = body->GetLinearVelocity();
	velocity->x = vec.x;
	velocity->y = vec.y;
}
